<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="utf-8">
  
  <title>高性能发号器-Vesta | 华锅锅的博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  
    <meta name="keywords" content="Huaguoguo,Huaguoguo's Blog">
  
  <meta name="description" content="架构设计摘自作者李艳鹏的gitee开源项目vesta-id-generator 前言在互联网世界里，一个生产唯一ID的的服务成为发号器，本文将围绕一款专业的开源项目Vesta讲解发号器的架构设计思想 架构设计 当前遇到的问题当前业务系统的ID使用数据库的自增字段，自增字段完全依赖于数据库，这在数据库移植，扩容，洗数据，分库分表等操作时带来了很多麻烦。 在数据库分库分表时，有一种办法是通过调整自增字">
<meta name="keywords" content="java,分布式">
<meta property="og:type" content="article">
<meta property="og:title" content="高性能发号器-Vesta">
<meta property="og:url" content="http://huaguoguo.gitee.io/2019/05/05/id-gen/index.html">
<meta property="og:site_name" content="华锅锅的博客">
<meta property="og:description" content="架构设计摘自作者李艳鹏的gitee开源项目vesta-id-generator 前言在互联网世界里，一个生产唯一ID的的服务成为发号器，本文将围绕一款专业的开源项目Vesta讲解发号器的架构设计思想 架构设计 当前遇到的问题当前业务系统的ID使用数据库的自增字段，自增字段完全依赖于数据库，这在数据库移植，扩容，洗数据，分库分表等操作时带来了很多麻烦。 在数据库分库分表时，有一种办法是通过调整自增字">
<meta property="og:locale" content="default">
<meta property="og:updated_time" content="2019-05-11T12:06:22.051Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="高性能发号器-Vesta">
<meta name="twitter:description" content="架构设计摘自作者李艳鹏的gitee开源项目vesta-id-generator 前言在互联网世界里，一个生产唯一ID的的服务成为发号器，本文将围绕一款专业的开源项目Vesta讲解发号器的架构设计思想 架构设计 当前遇到的问题当前业务系统的ID使用数据库的自增字段，自增字段完全依赖于数据库，这在数据库移植，扩容，洗数据，分库分表等操作时带来了很多麻烦。 在数据库分库分表时，有一种办法是通过调整自增字">
  
  
    <link rel="icon" href="/favicon.ico">
  
  <link href="//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="/css/style.css">
  <script src="/js/pace.min.js"></script>
  

  
  

</head>
</html>
<body>
  <div id="container">
      <header id="header">
    <div id="banner"></div>
    <div id="header-outer">
        <div id="header-menu" class="header-menu-pos animated">
            <div class="header-menu-container">
                <a href="/" class="left">
                    <span class="site-title">Huaguoguo&#39;s Blog</span>
                </a>
                <nav id="header-menu-nav" class="right">
                    
                    <a href="/">
                        <i class="fa fa-home"></i>
                        <span>Home</span>
                    </a>
                    
                    <a href="/archives">
                        <i class="fa fa-archive"></i>
                        <span>Archives</span>
                    </a>
                    
                    <a href="/about">
                        <i class="fa fa-user"></i>
                        <span>About</span>
                    </a>
                    
                </nav>
                <a class="mobile-header-menu-button">
                    <i class="fa fa-bars"></i>
                </a>
            </div>
        </div>
        <div id="header-row">
            <div id="logo">
                <a href="/">
                    <img src="/images/logo.png" alt="logo">
                </a>
            </div>
            <div class="header-info">
                <div id="header-title">
                    
                    <h2>
                        Huaguoguo&#39;s Blog
                    </h2>
                    
                </div>
                <div id="header-description">
                    
                    <h3>
                        敲代码是热爱，敲到世界充满爱
                    </h3>
                    
                </div>
            </div>
            <nav class="header-nav">
                <div class="social">
                    
                        <a title="home" target="_blank" href="//huaguoguo.gitee.io">
                            <i class="fa fa-home fa-2x"></i></a>
                    
                        <a title="Github" target="_blank" href="//gitee.com/huaguoguo">
                            <i class="fa fa-github fa-2x"></i></a>
                    
                </div>
            </nav>
        </div>
    </div>
</header>
      <div class="outer">
        <section id="main" class="body-wrap"><article id="post-id-gen" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 class="post-title" itemprop="name">
      高性能发号器-Vesta
    </h1>
    <div class="post-title-bar">
      <ul>
          
        <li>
          <i class="fa fa-calendar"></i>  2019-05-05
        </li>
        <li>
          <i class="fa fa-eye"></i>
          <span id="busuanzi_value_page_pv"></span>
        </li>
      </ul>
    </div>
  

          
      </header>
    
    <div class="article-entry post-content" itemprop="articleBody">
      
            
            <p>架构设计摘自作者李艳鹏的gitee开源项目<a href="https://gitee.com/robertleepeak/vesta-id-generator" target="_blank" rel="noopener">vesta-id-generator</a></p>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在互联网世界里，一个生产唯一ID的的服务成为发号器，本文将围绕一款专业的开源项目Vesta讲解发号器的架构设计思想</p>
<h1 id="架构设计"><a href="#架构设计" class="headerlink" title="架构设计"></a>架构设计</h1><hr>
<h2 id="当前遇到的问题"><a href="#当前遇到的问题" class="headerlink" title="当前遇到的问题"></a>当前遇到的问题</h2><p>当前业务系统的ID使用数据库的自增字段，自增字段完全依赖于数据库，这在数据库移植，扩容，洗数据，分库分表等操作时带来了很多麻烦。</p>
<p>在数据库分库分表时，有一种办法是通过调整自增字段或者数据库sequence的步长来达到跨数据库的ID的唯一性，但仍然是一种强依赖数据库的解决方案，有诸多的限制，并且强依赖数据库类型，我们并不推荐这种方法。</p>
<h2 id="为什么不用UUID"><a href="#为什么不用UUID" class="headerlink" title="为什么不用UUID"></a>为什么不用UUID</h2><p>UUID虽然能够保证ID的唯一性，但是，它无法满足业务系统需要的很多其他特性，例如：时间粗略有序性，可反解和可制造型。另外，UUID产生的时候使用完全的时间数据，性能比较差，并且UUID比较长，占用空间大，间接导致数据库性能下降，更重要的是，UUID并不具有有序性，这导致B+树索引在写的时候会有过多的随机写操作（连续的ID会产生部分顺序写），另外写的时候由于不能产生顺序的append操作，需要进行insert操作，这会读取整个B+树节点到内存，然后插入这条记录后写整个节点回磁盘，这种操作在记录占用空间比较大的情况下，性能下降比较大</p>
<h2 id="需求整理"><a href="#需求整理" class="headerlink" title="需求整理"></a>需求整理</h2><p>既然数据库自增ID和UUID有诸多的限制，我们需要整理一下发号器的需求。</p>
<p><strong>1. 全局唯一</strong></p>
<p>有些业务系统可以使用相对小范围的唯一性，例如，如果用户是唯一的，那么同一用户的订单采用自增序列在用户范围内也是唯一的，但是如果这样设计，订单系统就会在逻辑上依赖用户系统，因此，不如我们保证ID在系统范围内的全局唯一性更实用。</p>
<p>分布式系统保证全局唯一的一个悲观策略是使用锁或者分布式锁，但是，只要使用了锁，就会大大的降低性能。</p>
<p>因此，我们决定利用时间的有序性，并且在时间的某个单元下采用自增序列，达到全局的唯一性。</p>
<p><strong>2. 粗略有序</strong></p>
<p>上面讨论了UUID的最大问题就是无序的，任何业务都希望生成的ID是有序的，但是，分布式系统中要做到完全有序，就涉及到数据的汇聚，当然要用到锁或者布式锁，考虑到效率，只能采用折中的方案，粗略有序，到底有多粗略，目前有两种主流的方案，一种是秒级有序，一种是毫秒级有序，这里又有一个权衡和取舍，我们决定支持两种方式，通过配置来决定服务使用其中的一种方式。</p>
<p><strong>3. 可反解</strong></p>
<p>一个 ID 生成之后，ID本身带有很多信息量，线上排查的时候，我们通常首先看到的是ID，如果根据ID就能知道什么时候产生的，从哪里来的，这样一个可反解的 ID 可以帮上很多忙。</p>
<p>如果ID 里有了时间而且能反解，在存储层面就会省下很多传统的timestamp 一类的字段所占用的空间了，这也是一举两得的设计。</p>
<p><strong>4. 可制造</strong></p>
<p>一个系统即使再高可用也不会保证永远不出问题，出了问题怎么办，手工处理，数据被污染怎么办，洗数据，可是手工处理或者洗数据的时候，假如使用数据库自增字段，ID已经被后来的业务覆盖了，怎么恢复到系统出问题的时间窗口呢？</p>
<p>所以，我们使用的发号器一定要可复制，可恢复 ，可制造。</p>
<p><strong>5. 高性能</strong></p>
<p>不管哪个业务，订单也好，商品也好，如果有新记录插入，那一定是业务的核心功能，对性能的要求非常高，ID生成取决于网络IO和CPU的性能，CPU一般不是瓶颈，根据经验，单台机器TPS应该达到10000/s。</p>
<p><strong>6. 高可用</strong></p>
<p>首先，发号器必须是一个对等的集群，一台机器挂掉，请求必须能够转发到其他机器，另外，重试机制也是必不可少的。最后，如果远程服务宕机，我们需要有本地的容错方案，本地库的依赖方式可以作为高可用的最后一道屏障。</p>
<p><strong>7. 可伸缩</strong></p>
<p>作为一个分布式系统，永远都不能忽略的就是业务在不断地增长，业务的绝对容量不是衡量一个系统的唯一标准，要知道业务是永远增长的，所以，系统设计不但要考虑能承受的绝对容量，还必须考虑业务增长的速度，系统的水平伸缩是否能满足业务的增长速度是衡量一个系统的另一个重要标准。 </p>
<h2 id="设计与实现"><a href="#设计与实现" class="headerlink" title="设计与实现"></a>设计与实现</h2><h3 id="发布模式"><a href="#发布模式" class="headerlink" title="发布模式"></a>发布模式</h3><p>根据最终的客户使用方式，可分为嵌入发布模式，中心服务器发布模式和REST发布模式。</p>
<ol>
<li><p><strong>嵌入发布模式</strong>：只适用于Java客户端，提供一个本地的Jar包，Jar包是嵌入式的原生服务，需要提前配置本地机器ID（或者服务启动时候Zookeeper动态分配唯一的ID,在第二版中实现)，但是不依赖于中心服务器。</p>
</li>
<li><p><strong>中心服务器发布模式</strong>：只适用于Java客户端，提供一个服务的客户端Jar包，Java程序像调用本地API一样来调用，但是依赖于中心的ID产生服务器。</p>
</li>
<li><p><strong>REST发布模式</strong>：中心服务器通过Restful API导出服务，供非Java语言客户端使用。</p>
</li>
</ol>
<p>发布模式最后会记录在生成的ID中。也参考下面数据结构段的发布模式相关细节。</p>
<h3 id="ID类型"><a href="#ID类型" class="headerlink" title="ID类型"></a>ID类型</h3><p>根据时间的位数和序列号的位数，可分为最大峰值型和最小粒度型。</p>
<p><strong>1. 最大峰值型</strong>：采用秒级有序，秒级时间占用30位，序列号占用20位</p>
<table>
<thead>
<tr>
<th style="text-align:center"><code>字段</code></th>
<th style="text-align:center"><code>版本</code></th>
<th style="text-align:center"><code>类型</code></th>
<th style="text-align:center"><code>生成方式</code></th>
<th style="text-align:center"><code>秒级时间</code></th>
<th style="text-align:center"><code>序列号</code></th>
<th style="text-align:center"><code>机器ID</code></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><em>位数</em></td>
<td style="text-align:center"><em>63</em></td>
<td style="text-align:center"><em>62</em></td>
<td style="text-align:center"><em>60-61</em></td>
<td style="text-align:center"><em>40-59</em></td>
<td style="text-align:center"><em>10-39</em></td>
<td style="text-align:center"><em>0-9</em></td>
</tr>
</tbody>
</table>
<p><strong>2. 最小粒度型</strong>：采用毫秒级有序，毫秒级时间占用40位，序列号占用10位</p>
<table>
<thead>
<tr>
<th style="text-align:center"><code>字段</code></th>
<th style="text-align:center"><code>版本</code></th>
<th style="text-align:center"><code>类型</code></th>
<th style="text-align:center"><code>生成方式</code></th>
<th style="text-align:center"><code>毫秒级时间</code></th>
<th style="text-align:center"><code>序列号</code></th>
<th style="text-align:center"><code>机器ID</code></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><em>位数</em></td>
<td style="text-align:center"><em>63</em></td>
<td style="text-align:center"><em>62</em></td>
<td style="text-align:center"><em>60-61</em></td>
<td style="text-align:center"><em>20-59</em></td>
<td style="text-align:center"><em>10-19</em></td>
<td style="text-align:center"><em>0-9</em></td>
</tr>
</tbody>
</table>
<p>最大峰值型能够承受更大的峰值压力，但是粗略有序的粒度有点大，最小粒度型有较细致的粒度，但是每个毫秒能承受的理论峰值有限，为1k，同一个毫秒如果有更多的请求产生，必须等到下一个毫秒再响应。</p>
<p>ID类型在配置时指定，需要重启服务才能互相切换。</p>
<h3 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h3><p><strong>1. 序列号</strong></p>
<p><em><code>最大峰值型</code></em></p>
<p>20位，理论上每秒内平均可产生2^20= 1048576个ID，百万级别，如果系统的网络IO和CPU足够强大，可承受的峰值达到每毫秒百万级别。</p>
<p><em><code>最小粒度型</code></em></p>
<p>10位，每毫秒内序列号总计2^10=1024个, 也就是每个毫秒最多产生1000+个ID，理论上承受的峰值完全不如我们最大峰值方案。</p>
<p><strong>2. 秒级时间/毫秒级时间</strong></p>
<p><em><code>最大峰值型</code></em></p>
<p>30位，表示秒级时间，2^30/60/60/24/365=34，也就是可使用30+年。</p>
<p><em><code>最小粒度型</code></em></p>
<p>40位，表示毫秒级时间，2^40/1000/60/60/24/365=34，同样可以使用30+年。</p>
<p><strong>3. 机器ID</strong></p>
<p>10位， 2^10=1024, 也就是最多支持1000+个服务器。中心发布模式和REST发布模式一般不会有太多数量的机器，按照设计每台机器TPS 1万/s，10台服务器就可以有10万/s的TPS，基本可以满足大部分的业务需求。</p>
<p>但是考虑到我们在业务服务可以使用内嵌发布方式，对机器ID的需求量变得更大，这里最多支持1024个服务器。</p>
<p><strong>4. 生成方式</strong></p>
<p>2位，用来区分三种发布模式：嵌入发布模式，中心服务器发布模式，REST发布模式。</p>
<blockquote>
<p><strong>00</strong>：嵌入发布模式<br><strong>01</strong>：中心服务器发布模式<br><strong>02</strong>：REST发布模式<br><strong>03</strong>：保留未用</p>
</blockquote>
<p><strong>5. ID类型</strong></p>
<p>1位，用来区分两种ID类型：最大峰值型和最小粒度型。</p>
<blockquote>
<p><strong>0</strong>：最大峰值型<br><strong>1</strong>：最小粒度型</p>
</blockquote>
<p><strong>6. 版本</strong></p>
<p>1位，用来做扩展位或者扩容时候的临时方案。</p>
<blockquote>
<p><strong>0</strong>：默认值，以免转化为整型再转化回字符串被截断<br><strong>1</strong>：表示扩展或者扩容中 </p>
</blockquote>
<p>作为30年后扩展使用，或者在30年后ID将近用光之时，扩展为秒级时间或者毫秒级时间来挣得系统的移植时间窗口，其实只要扩展一位，完全可以再使用30年。</p>
<h3 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h3><p>对于中心服务器和REST发布方式，ID生成的过程涉及到网络IO和CPU操作，ID的生成基本都是内存到高速缓存的操作，没有IO操作，网络IO是系统的瓶颈。</p>
<p>相对于CPU计算速度来说网络IO是瓶颈，因此，ID产生的服务使用多线程的方式，对于ID生成过程中的竞争点time和sequence，这里使用了多种实现方式</p>
<blockquote>
<ol>
<li>使用concurrent包的ReentrantLock进行互斥，这是缺省的实现方式，也是追求性能和稳定两个目标的妥协方案。</li>
<li>使用传统的synchronized进行互斥，这种方式的性能稍微逊色一些，通过传入JVM参数-Dvesta.sync.lock.impl.key=true来开启。</li>
<li>使用concurrent包的ReentrantLock进行互斥，这种实现方式的性能非常高，但是在高并发环境下CPU负载会很高，通过传入JVM参数-Dvesta.atomic.impl.key=true来开启。</li>
</ol>
</blockquote>
<h3 id="机器ID的分配"><a href="#机器ID的分配" class="headerlink" title="机器ID的分配"></a>机器ID的分配</h3><p>我们将机器ID分为两个区段，一个区段服务于中心服务器发布模式和REST发布模式，另外一个区段服务于嵌入发布模式。</p>
<p>0-923：嵌入发布模式，预先配置，（或者由Zookeeper产生，第二版中实现），最多支持924台内嵌服务器<br>924 – 1023：中心服务器发布模式和REST发布模式，最多支持300台，最大支持300*1万=300万/s的TPS</p>
<p>如果嵌入式发布模式和中心服务器发布模式以及REST发布模式的使用量不符合这个比例，我们可以动态调整两个区间的值来适应。</p>
<p>另外，各个垂直业务之间具有天生的隔离性，每个业务都可以使用最多1024台服务器。</p>
<h3 id="与Zookeeper集成"><a href="#与Zookeeper集成" class="headerlink" title="与Zookeeper集成"></a>与Zookeeper集成</h3><p>对于嵌入发布模式，服务启动需要连接Zookeeper集群，Zookeeper分配一个0-923区间的一个ID，如果0-923区间的ID被用光，Zookeeper会分配一个大于923的ID，这种情况，拒绝启动服务。</p>
<p>如果不想使用Zookeeper产生的唯一的机器ID，我们提供缺省的预配的机器ID解决方案，每个使用统一发号器的服务需要预先配置一个默认的机器ID。</p>
<p>注：此功能在第二版中实现。</p>
<h3 id="时间同步"><a href="#时间同步" class="headerlink" title="时间同步"></a>时间同步</h3><p>使用Linux的定时任务crontab，定时通过授时服务器虚拟集群（全球有3000多台服务器）来核准服务器的时间。</p>
<blockquote>
<p><strong>ntpdate -u pool.ntp.orgpool.ntp.org</strong></p>
</blockquote>
<h2 id="设计验证"><a href="#设计验证" class="headerlink" title="设计验证"></a>设计验证</h2><ol>
<li>我们根据不同的信息分段构建一个ID，使ID具有全局唯一，可反解和可制造。</li>
<li>我们使用秒级别时间或者毫秒级别时间以及时间单元内部序列递增的方法保证ID粗略有序。</li>
<li>对于中心服务器发布模式和REST发布模式，我们使用多线程处理，为了减少多线程间竞争，我们对竞争点time和sequence使用ReentrantLock来进行互斥，由于ReentrantLock内部使用CAS，这比JVM的Synchronized关键字性能更好，在千兆网卡的前提下，至少可达到1万/s以上的TPS。</li>
<li>由于我们支持中心服务器发布模式，嵌入式发布模式和REST发布模式，如果某种模式不可用，可以回退到其他发布模式，如果Zookeeper不可用，可以会退到使用本地预配的机器ID。从而达到服务的最大可用。</li>
<li>由于ID的设计，我们最大支持1024台服务器，我们将服务器机器号分为两个区段，一个从0开始向上，一个从128开始向下，并且能够动态调整分界线，满足了可伸缩性。</li>
</ol>
<h2 id="性能需求"><a href="#性能需求" class="headerlink" title="性能需求"></a>性能需求</h2><p>最终的性能验证要保证每台服务器的TPS达到1万/s以上。</p>
<h2 id="FAQ"><a href="#FAQ" class="headerlink" title="FAQ"></a>FAQ</h2><ol>
<li>调整时间是否会影响ID产生功能？</li>
</ol>
<blockquote>
<ol>
<li>未重启机器调慢时间，Vesta抛出异常，拒绝产生ID。重启机器调快时间，调整后正常产生ID，调整时段内没有ID产生。</li>
<li>重启机器调慢时间，Vesta将可能产生重复的时间，系统管理员需要保证不会发生这种情况。重启机器调快时间，调整后正常产生ID，调整时段内没有ID产生。</li>
</ol>
</blockquote>
<ol start="2">
<li>每4年一次同步润秒会不会影响ID产生功能？</li>
</ol>
<blockquote>
<ol>
<li>原子时钟和电子时钟每四年误差为1秒，也就是说电子时钟每4年会比原子时钟慢1秒，所以，每隔四年，网络时钟都会同步一次时间，但是本地机器Windows,Linux等不会自动同步时间，需要手工同步，或者使用ntpupdate向网络时钟同步。</li>
</ol>
</blockquote>
<ol start="2">
<li>由于时钟是调快1秒，调整后不影响ID产生，调整的1s内没有ID产生。</li>
</ol>
<h1 id="服务接口实现"><a href="#服务接口实现" class="headerlink" title="服务接口实现"></a>服务接口实现</h1><h2 id="IdService"><a href="#IdService" class="headerlink" title="IdService"></a>IdService</h2><p>构建了一个工厂方法类IdServiceFactoryBean，通过spring框架的FactoryBean来实现，将IdService创建过程都内聚在其中</p>
<h2 id="生成id主逻辑"><a href="#生成id主逻辑" class="headerlink" title="生成id主逻辑"></a>生成id主逻辑</h2><p>抽象出一个骨架，有不同逻辑的给出一个模板回调方法由子类去实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">genId</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Id id = <span class="keyword">new</span> Id();</span><br><span class="line">    <span class="comment">// 构造id元数据</span></span><br><span class="line">    id.setMachine(machineId);</span><br><span class="line">    id.setGenMethod(genMethod);</span><br><span class="line">    id.setType(idType.value());</span><br><span class="line">    id.setVersion(version);</span><br><span class="line">    <span class="comment">// 模板方法，由子类去实现各自业务场景生成时间和序列号的算法</span></span><br><span class="line">    populateId(id);</span><br><span class="line">    <span class="comment">// 元数据转换成数字id</span></span><br><span class="line">    <span class="keyword">long</span> ret = idConverter.convert(id, <span class="keyword">this</span>.idMeta);</span><br><span class="line">    <span class="keyword">if</span> (log.isTraceEnabled()) &#123;</span><br><span class="line">        log.trace(String.format(<span class="string">"Id: %s =&gt; %d"</span>, id, ret));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="IdPopulator"><a href="#IdPopulator" class="headerlink" title="IdPopulator"></a>IdPopulator</h2><p>计算时间和序列号的接口，待补充UML图</p>
<p>提供了三种线程安全的实现类</p>
<p>LockIdPopulator，SyncIdPopulator都是通过同步锁synchronized来进行同步，性能稍微逊色一些</p>
<p>AtomicIdPopulator通过CAS实现了无锁版本，把时间和序列号两个字段封装到AtomicReference中，大致实现逻辑如下：</p>
<ol>
<li>取得并保存旧值varOld，这个变量包含了原来的时间和序列号；</li>
<li>基于旧值计算新的时间和序列号</li>
<li>使用AtomicReference对象的compareAndSet(varOld, varNew)操作更新旧值</li>
<li>如果保存的旧值变量被其他线程修改了，就重复以上逻辑</li>
</ol>
<h2 id="IdConverter"><a href="#IdConverter" class="headerlink" title="IdConverter"></a>IdConverter</h2><p>ID元数据和数字ID的转换类</p>
<h3 id="ID元数据-gt-数字ID"><a href="#ID元数据-gt-数字ID" class="headerlink" title="ID元数据 =&gt; 数字ID"></a>ID元数据 =&gt; 数字ID</h3><p>通过id元数据信息对象获取每个属性所在ID的位数，然后通过左移来实现将各个属性拼接到一个长整型的数字里</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">long</span> <span class="title">doConvert</span><span class="params">(Id id, IdMeta idMeta)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> ret = <span class="number">0</span>;</span><br><span class="line">    ret |= id.getMachine();</span><br><span class="line">    ret |= id.getSeq() &lt;&lt; idMeta.getSeqBitsStartPos();</span><br><span class="line">    ret |= id.getTime() &lt;&lt; idMeta.getTimeBitsStartPos();</span><br><span class="line">    ret |= id.getGenMethod() &lt;&lt; idMeta.getGenMethodBitsStartPos();</span><br><span class="line">    ret |= id.getType() &lt;&lt; idMeta.getTypeBitsStartPos();</span><br><span class="line">    ret |= id.getVersion() &lt;&lt; idMeta.getVersionBitsStartPos();</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="数字ID-gt-ID元数据"><a href="#数字ID-gt-ID元数据" class="headerlink" title="数字ID =&gt; ID元数据"></a>数字ID =&gt; ID元数据</h3><p>反解时使用的是无符号右移操作，另外使用了屏蔽字，用于从ID数字中取出我们想要的某个属性的值，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> Id <span class="title">doConvert</span><span class="params">(<span class="keyword">long</span> id, IdMeta idMeta)</span> </span>&#123;</span><br><span class="line">    Id ret = <span class="keyword">new</span> Id();</span><br><span class="line">    ret.setMachine(id &amp; idMeta.getMachineBitsMask());</span><br><span class="line">    ret.setSeq((id &gt;&gt;&gt; idMeta.getSeqBitsStartPos()) &amp; idMeta.getSeqBitsMask());</span><br><span class="line">    ret.setTime((id &gt;&gt;&gt; idMeta.getTimeBitsStartPos()) &amp; idMeta.getTimeBitsMask());</span><br><span class="line">    ret.setGenMethod((id &gt;&gt;&gt; idMeta.getGenMethodBitsStartPos()) &amp; 					idMeta.getGenMethodBitsMask());</span><br><span class="line">    ret.setType((id &gt;&gt;&gt; idMeta.getTypeBitsStartPos()) &amp; idMeta.getTypeBitsMask());</span><br><span class="line">    ret.setVersion((id &gt;&gt;&gt; idMeta.getVersionBitsStartPos()) &amp; idMeta.getVersionBitsMask());</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>举个栗子，假设ID的生成方式Type为11111</p>
<ol>
<li><p>首先把ID数字整体右移，直到Type位于最右端</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">id &gt;&gt;&gt; idMeta.getTypeBitsStartPos()</span><br></pre></td></tr></table></figure>
</li>
<li><p>然后与屏蔽字进行与操作，得到的结果为Type属性值</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(id &gt;&gt;&gt; idMeta.getTypeBitsStartPos()) &amp; idMeta.getTypeBitsMask()</span><br></pre></td></tr></table></figure>
<p>屏蔽字的计算方式如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">getGenMethodBitsMask</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1L</span> ^ -<span class="number">1L</span> &lt;&lt; genMethodBits;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li>
</ol>

            <div class="post-copyright">
    <div class="content">
        <p>最后更新： 2019年05月11日 20:06</p>
        <p>原始链接： <a class="post-url" href="/2019/05/05/id-gen/" title="高性能发号器-Vesta">http://huaguoguo.gitee.io/2019/05/05/id-gen/</a></p>
        <footer>
            <a href="http://huaguoguo.gitee.io">
                <img src="/images/logo.png" alt="华锅锅">
                华锅锅
            </a>
        </footer>
    </div>
</div>

      
        
            
<div class="page-reward">
    <a id="rewardBtn" href="javascript:;">赏</a>
</div>

<div id="reward" class="post-modal reward-lay">
    <a class="close" href="javascript:;" id="reward-close">×</a>
    <span class="reward-title">
        <i class="icon icon-quote-left"></i>
        请我吃糖~
        <i class="icon icon-quote-right"></i>
    </span>
    <div class="reward-content">
        
        <div class="reward-code">
            <img id="rewardCode" src="/images/wechat_code.png" alt="打赏二维码">
        </div>
        <div class="reward-select">
            
            <label class="reward-select-item checked" data-id="wechat" data-wechat="/images/wechat_code.png">
                <img class="reward-select-item-wechat" src="/images/wechat.png" alt="微信">
            </label>
            
            
            <label class="reward-select-item" data-id="alipay" data-alipay="/images/alipay_code.png">
                <img class="reward-select-item-alipay" src="/images/alipay.png" alt="支付宝">
            </label>
            
        </div>
    </div>
</div>


        
    </div>
    <footer class="article-footer">
        
        
<div class="post-share">
    <a href="javascript:;" id="share-sub" class="post-share-fab">
        <i class="fa fa-share-alt"></i>
    </a>
    <div class="post-share-list" id="share-list">
        <ul class="share-icons">
          <li>
            <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://huaguoguo.gitee.io/2019/05/05/id-gen/&title=《高性能发号器-Vesta》 — 华锅锅的博客&pic=/images/4310879-d81a617c8d634048.png" data-title="微博">
              <i class="fa fa-weibo"></i>
            </a>
          </li>
          <li>
            <a class="weixin share-sns" id="wxFab" href="javascript:;" data-title="微信">
              <i class="fa fa-weixin"></i>
            </a>
          </li>
          <li>
            <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://huaguoguo.gitee.io/2019/05/05/id-gen/&title=《高性能发号器-Vesta》 — 华锅锅的博客&source=敲代码是热爱，敲到世界充满爱" data-title="QQ">
              <i class="fa fa-qq"></i>
            </a>
          </li>
          <li>
            <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://huaguoguo.gitee.io/2019/05/05/id-gen/" data-title="Facebook">
              <i class="fa fa-facebook"></i>
            </a>
          </li>
          <li>
            <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《高性能发号器-Vesta》 — 华锅锅的博客&url=http://huaguoguo.gitee.io/2019/05/05/id-gen/&via=http://huaguoguo.gitee.io" data-title="Twitter">
              <i class="fa fa-twitter"></i>
            </a>
          </li>
          <li>
            <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://huaguoguo.gitee.io/2019/05/05/id-gen/" data-title="Google+">
              <i class="fa fa-google-plus"></i>
            </a>
          </li>
        </ul>
     </div>
</div>
<div class="post-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;" id="wxShare-close">×</a>
    <p>扫一扫，分享到微信</p>
    <img src="//api.qrserver.com/v1/create-qr-code/?data=http://huaguoguo.gitee.io/2019/05/05/id-gen/" alt="微信分享二维码">
</div>

<div class="mask"></div>

        
        <ul class="article-footer-menu">
            
            
  <li class="article-footer-tags">
    <i class="fa fa-tags"></i>
      
    <a href="/tags/java/" class="color5">java</a>
      
    <a href="/tags/分布式/" class="color4">分布式</a>
      
  </li>

        </ul>
        
    </footer>
  </div>
</article>


    <aside class="post-toc-pos post-toc-top" id="post-toc">
        <nav class="post-toc-wrap">
            <ol class="post-toc"><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#前言"><span class="post-toc-text">前言</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#架构设计"><span class="post-toc-text">架构设计</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#当前遇到的问题"><span class="post-toc-text">当前遇到的问题</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#为什么不用UUID"><span class="post-toc-text">为什么不用UUID</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#需求整理"><span class="post-toc-text">需求整理</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#设计与实现"><span class="post-toc-text">设计与实现</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#发布模式"><span class="post-toc-text">发布模式</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#ID类型"><span class="post-toc-text">ID类型</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#数据结构"><span class="post-toc-text">数据结构</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#并发"><span class="post-toc-text">并发</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#机器ID的分配"><span class="post-toc-text">机器ID的分配</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#与Zookeeper集成"><span class="post-toc-text">与Zookeeper集成</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#时间同步"><span class="post-toc-text">时间同步</span></a></li></ol></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#设计验证"><span class="post-toc-text">设计验证</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#性能需求"><span class="post-toc-text">性能需求</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#FAQ"><span class="post-toc-text">FAQ</span></a></li></ol></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#服务接口实现"><span class="post-toc-text">服务接口实现</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#IdService"><span class="post-toc-text">IdService</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#生成id主逻辑"><span class="post-toc-text">生成id主逻辑</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#IdPopulator"><span class="post-toc-text">IdPopulator</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#IdConverter"><span class="post-toc-text">IdConverter</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#ID元数据-gt-数字ID"><span class="post-toc-text">ID元数据 =&gt; 数字ID</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#数字ID-gt-ID元数据"><span class="post-toc-text">数字ID =&gt; ID元数据</span></a></li></ol></li></ol></li></ol>
        </nav>
    </aside>
    

<nav id="article-nav">
  
    <a href="/2019/05/11/redis/" id="article-nav-newer" class="article-nav-link-wrap">

      <span class="article-nav-title">
        <i class="fa fa-hand-o-left" aria-hidden="true"></i>
        
          redis学习整理
        
      </span>
    </a>
  
  
    <a href="/2019/05/04/Hexo-miho-build/" id="article-nav-older" class="article-nav-link-wrap">
      <span class="article-nav-title">Hexo+码云搭建个人博客</span>
      <i class="fa fa-hand-o-right" aria-hidden="true"></i>
    </a>
  
</nav>



    
        <div id="SOHUCS" sid="id-gen"></div>
<script type="text/javascript">
    (function(){
        var appid = 'cyudxaFKB';
        var conf = 'e3991feeac8273781b96fbec365fe014';
        var width = window.innerWidth || document.documentElement.clientWidth;
        if (width < 960) {
            window.document.write('<script id="changyan_mobile_js" charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/mobile/wap-js/changyan_mobile.js?client_id=' + appid + '&conf=' + conf + '"><\/script>'); } else { var loadJs=function(d,a){var c=document.getElementsByTagName("head")[0]||document.head||document.documentElement;var b=document.createElement("script");b.setAttribute("type","text/javascript");b.setAttribute("charset","UTF-8");b.setAttribute("src",d);if(typeof a==="function"){if(window.attachEvent){b.onreadystatechange=function(){var e=b.readyState;if(e==="loaded"||e==="complete"){b.onreadystatechange=null;a()}}}else{b.onload=a}}c.appendChild(b)};loadJs("https://changyan.sohu.com/upload/changyan.js",function(){window.changyan.api.config({appid:appid,conf:conf})}); } })(); </script>
    
</section>
        
      </div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info" class="inner">
      
<p>
    <span id="busuanzi_container_site_uv" style="display:none">
        总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style="display:none">
        总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


      <p>
        Powered by  <a href="http://hexo.io/" target="_blank">Hexo</a>
        Theme <a href="//github.com/wongminho/hexo-theme-miho" target="_blank">MiHo</a>
      &copy; 2020 华锅锅<br>
      </p>
    </div>
  </div>
</footer>
    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<script src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
  var mihoConfig = {
      root: "http://huaguoguo.gitee.io",
      animate: true,
      isHome: false,
      share: true,
      reward: 1
  }
</script>
<div class="sidebar">
    <div id="sidebar-search" title="Search">
        <i class="fa fa-search"></i>
    </div>
    <div id="sidebar-category" title="Categories">
        <i class="fa fa-book"></i>
    </div>
    <div id="sidebar-tag" title="Tags">
        <i class="fa fa-tags"></i>
    </div>
    <div id="sidebar-top">
        <span class="sidebar-top-icon"><i class="fa fa-angle-up"></i></span>
    </div>
</div>
<div class="sidebar-menu-box" id="sidebar-menu-box">
    <div class="sidebar-menu-box-container">
        <div id="sidebar-menu-box-categories">
            <a class="category-link" href="/categories/First/">First</a>
        </div>
        <div id="sidebar-menu-box-tags">
            <a href="/tags/Hexo/" style="font-size: 10px;">Hexo</a> <a href="/tags/Sample/" style="font-size: 10px;">Sample</a> <a href="/tags/java/" style="font-size: 20px;">java</a> <a href="/tags/分布式/" style="font-size: 15px;">分布式</a> <a href="/tags/学习方法/" style="font-size: 10px;">学习方法</a> <a href="/tags/数据库/" style="font-size: 15px;">数据库</a> <a href="/tags/缓存/" style="font-size: 10px;">缓存</a> <a href="/tags/集合/" style="font-size: 10px;">集合</a>
        </div>
    </div>
    <a href="javascript:;" class="sidebar-menu-box-close">&times;</a>
</div>
<div class="mobile-header-menu-nav" id="mobile-header-menu-nav">
    <div class="mobile-header-menu-container">
        <span class="title">Menus</span>
        <ul class="mobile-header-menu-navbar">
            
            <li>
                <a href="/">
                    <i class="fa fa-home"></i><span>Home</span>
                </a>
            </li>
            
            <li>
                <a href="/archives">
                    <i class="fa fa-archive"></i><span>Archives</span>
                </a>
            </li>
            
            <li>
                <a href="/about">
                    <i class="fa fa-user"></i><span>About</span>
                </a>
            </li>
            
        </ul>
    </div>
    <div class="mobile-header-tag-container">
        <span class="title">Tags</span>
        <div id="mobile-header-container-tags">
            <a href="/tags/Hexo/" style="font-size: 10px;">Hexo</a> <a href="/tags/Sample/" style="font-size: 10px;">Sample</a> <a href="/tags/java/" style="font-size: 20px;">java</a> <a href="/tags/分布式/" style="font-size: 15px;">分布式</a> <a href="/tags/学习方法/" style="font-size: 10px;">学习方法</a> <a href="/tags/数据库/" style="font-size: 15px;">数据库</a> <a href="/tags/缓存/" style="font-size: 10px;">缓存</a> <a href="/tags/集合/" style="font-size: 10px;">集合</a>
        </div>
    </div>
</div>
<div class="search-wrap">
    <span class="search-close">&times;</span>
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
            <i class="icon icon-lg icon-chevron-left"></i>
        </a>
        <input class="search-field" placeholder="Search..." id="keywords">
        <a id="search-submit" href="javascript:;">
            <i class="fa fa-search"></i>
        </a>
    <div class="search-container" id="search-container">
        <ul class="search-result" id="search-result">
        </ul>
    </div>
</div>

<div id="search-tpl">
    <li class="search-result-item">
        <a href="{url}" class="search-item-li">
            <span class="search-item-li-title" title="{title}">{title}</span>
        </a>
    </li>
</div>
<script src="/js/search.js"></script>
<script src="/js/main.js"></script>


  <script src="//cdn.bootcss.com/particles.js/2.0.0/particles.min.js"></script>
  <div id="particles"></div>
  <script src="/js/particles.js"></script>







  <link rel="stylesheet" href="//cdn.bootcss.com/animate.css/3.5.0/animate.min.css">
  <script src="//cdn.bootcss.com/scrollReveal.js/3.0.5/scrollreveal.js"></script>
  <script src="/js/animate.js"></script>


  <script src="/js/pop-img.js"></script>
  <script>
     $(".article-entry p img").popImg();
  </script>

  </div>
</body>
</html>